#include "scanner.h"

// ctor
Scanner::Scanner(string input) : input(input), row(1), column(1)
{
    this->initAutomat();
}

Scanner::Scanner() :  row(1), column(1)
{
    this->input = "";
    this->initAutomat();
}

void Scanner::setInput(string _input)
{
    this->input.assign(_input);
    this->nextToken();
}

void Scanner::initAutomat()  // inicializacija tabele prehodov in tabele koncnih stanj
{
    // Initialize table (empty)
    for(int i=0; i<=MAX_STATE; i++)
    {
            for(int j=0; j<256; j++)
            automata[i][j] = NO_EDGE;
    }

    //
    // Edge table:
    //
    // Number: 0 ... 9 in state 0 or 1 --> state 1
    for(int i='0'; i<='9'; i++)
            automata[0][i] = automata[1][i] = 1;



    // Same applies for '.', since numbers can be decimals
    automata[0]['.'] =  automata[1]['.'] = 1;

    // Operators: *,/,+,- in state 0 --> state 2
    automata[0]['+'] = automata[0]['*'] = automata[0]['-'] =
    automata[0]['/'] = automata[0]['='] = 2;

    // Separators: [,],(,) in state 0 --> state 3
    automata[0]['('] = automata[0][')'] = automata[0]['['] =
    automata[0][']'] = automata[0]['{'] = automata[0]['}'] =
    automata[0][','] = automata[0][':'] = 3;

    // String
    for(int i='a'; i<='z'; i++)
            automata[0][i] = automata[4][i] = 4;

    automata[4]['.'] = 4; // For filenames e.g. wall.tga

     automata[4]['_'] = 4;

    for(int i='0'; i<='9'; i++)
        automata[4][i] = 4;



    // Ignoreable character, state 5
    automata[0]['\n'] = automata[0][' '] = automata[0]['\t'] = 6;

    //
    // Finite states:
    //
    // Error, 0 is not an final state
    finite[0] = LEXERROR;

    // State 1: Number
    finite[1] = INTEGER;

    // State 2: Operator
    finite[2] = OPERATOR;

    // State 3: Operator
    finite[3] = SEPARATOR;

    // State 4: String
    finite[4] = STRING;

    // State 4: String
    finite[5] = IDENTIFICATOR;

    // State 5: Ignore
    finite[6] = IGNORE;

    //
    // Reserwed words:
    //

    // sin | cos | exp |sqrt | tan | log
    // translate, rotate, def
    // begind, end


    // var	update	object	as	with at	material
    // density	restitution	rectangle	circle	vertices
    // color	texture	border	fill static dynamic

    // Functions
    this->reswdWords[0] = "sin";
    this->reswdWords[1] = "cos";
    this->reswdWords[2] = "abs";
    this->reswdWords[3] = "exp";
    this->reswdWords[4] = "sqrt";
    this->reswdWords[5] = "tan";
    this->reswdWords[6] = "log";

    // Body parts
    this->reswdWords[7] = "left_arm0";
    this->reswdWords[8] = "left_arm1";

    this->reswdWords[9] = "right_arm0";
    this->reswdWords[10] = "right_arm1";

    this->reswdWords[11] = "left_leg0";
    this->reswdWords[12] = "left_leg1";

    this->reswdWords[13] = "right_leg0";
    this->reswdWords[14] = "right_leg1";

    this->reswdWords[15] = "torso";

    this->reswdWords[16] = "translate";
    this->reswdWords[17] = "rotate";
    this->reswdWords[18] = "def";

    this->reswdWords[19] = "begin";
    this->reswdWords[20] = "end";

    this->reswdWords[21] = "var";
    this->reswdWords[22] = "update";
    this->reswdWords[23] = "object";
    this->reswdWords[24] = "as";
    this->reswdWords[25] = "with";
    this->reswdWords[26] = "at";
    this->reswdWords[27] = "material";
    this->reswdWords[28] = "density";
    this->reswdWords[29] = "restitution";
    this->reswdWords[30] = "rectangle";
    this->reswdWords[31] = "circle";
    this->reswdWords[32] = "vertices";
    this->reswdWords[33] = "color";
    this->reswdWords[34] = "texture";
    this->reswdWords[35] = "border";
    this->reswdWords[36] = "fill";
    this->reswdWords[37] = "static";
    this->reswdWords[38] = "dynamic";
}

bool Scanner::isReservedWord(string s)
{
    for(int i=0; i<RESERWED_WORDS; i++)
            if(reswdWords[i] == s)
            return true;

    return false;
}

// naaslednje stanje vemo, ce poznamo trenutno stanje in znak na vhodu
// z pogledom v tabelo
int Scanner::getNextState(int aState, int aChar) const
{
    if(aChar == -2)
            return NO_EDGE;

    return this->automata[aState][aChar]; // achar je dejansko ascii vrednost ;)
}

// ali je koncno stanje?
bool Scanner::isFiniteState(int aState) const
{
    return this->finite[aState] != LEXERROR;
}

int Scanner::getFiniteState(int aState) const
{
    return finite[aState];
}

// preberi naslednji znak na vhodu
int Scanner::read()
{
    int firstChar = (int)input[0]; // beri prvi znak

    // pobrisi ga
    input.erase(0, 1);
    column ++; // stolpec ++

	// newline
    if(firstChar == '\n')
    {
            row ++; // vrstica = vrstica +1
            column = 1; // stolpec = 1;
    }

    return firstChar; // vrni prebrani prvi znak
}

bool Scanner::eof()
{
    return input.size() == 0 ? true : false; //peek() == -1; // ce je znak, ki ga preberemom, leks. -1, smo na koncu
}

int Scanner::peek()
{
    //    return input->peek(); // samp prebere znak v inputu in ga vrne, ga ne brise
    return (int)input[0]; //[input.size()-1];
}

Token Scanner::nextToken()
{
    return lastToken = nextTokenImp();
}

Token Scanner::currentToken()
{
    return lastToken;
}


Token Scanner::nextTokenImp()
{
    int currentState = START_STATE;
    string lexem;

    int startColumn = this->column;
    int startRow = this->row;

    do
    {
            // ob trenutnem stanju in vhodnemu znaku pojdi v novo stanje
            int tempState = this->getNextState(currentState, peek());

            if (tempState != NO_EDGE)
            {
            	// prehod v novo stanje mozen
            	currentState = tempState;

            	// zdruzi prebrani znak v lexem
            	lexem += this->peek();
            	(char)this->read();
            }
            else
            {
            	// prehod ni mozen; ali je stanje koncno?
            	if(this->isFiniteState(currentState))
            	{
                    // stanje je koncno in vracamo osonovni leks. simbol
                    // se je lex. simbol niz, preverimo, ce je rezervirana beseda
                    Token token(lexem, startColumn, startRow, this->getFiniteState(currentState), eof());

                    if(token.getType() == IGNORE)
                    	// presledke in nove vrstice prezremo
                    	return this->nextToken();
                    else
                    {
                    	if(currentState == STRING)
                    	{
                            if(!this->isReservedWord(lexem))
                                // If it isn't and reserwed word, it must be an identificator!
                                token.setCustomType (IDENTIFICATOR);
                    	}

                    	return token;
                    }
            	}
            else
                    // stanje ni koncno, vrnemo leks. napako
                    return Token("ERROR:" + lexem, startColumn, startRow,  LEXERROR, eof());

            }
    } while(true);
}




